package org.slusk.thynwor.neat;

import java.io.File;
import java.io.ObjectInputStream.GetField;

import org.jgap.Chromosome;

import com.anji.hyperneat.nd.ActivatorND;
import com.anji.hyperneat.nd.HyperNEATFitnessFunctionND;
import com.anji.hyperneat.nd.NDFloatArray;
import com.anji.hyperneat.nd.NDFloatArray.MatrixIterator;
import com.anji.neat.Evolver;
import com.anji.util.Properties;

public class poff extends HyperNEATFitnessFunctionND {
	private static final int maxFitnessValue = 1000;
	
	private static final int[][][][] inputs  = new int[][][][] {
		{// set 0
			{
				{1,0}
				,{0,0}
			}
			,{
				{0,0}
				,{0,0}
			}
		}
		,{// set 1
			{
				{0,1}
				,{0,0}
			}
			,{
				{0,0}
				,{0,0}
			}
		}	
		,{// set 2
			{
				{0,0}
				,{0,1}
			}
			,{
				{0,0}
				,{0,0}
			}
		}	
		,{// set 3
			{
				{0,0}
				,{1,0}
			}
			,{
				{0,0}
				,{0,0}
			}
		}	
		,{// set 4
			{
				{0,0}
				,{0,0}
			}
			,{
				{1,0}
				,{0,0}
			}
		}	
		,{// set 5
			{
				{0,0}
				,{0,0}
			}
			,{
				{0,1}
				,{0,0}
			}
		}	
		,{// set 6
			{
				{0,0}
				,{0,0}
			}
			,{
				{0,0}
				,{0,1}
			}
		}	
		,{// set 7
			{
				{0,0}
				,{0,0}
			}
			,{
				{0,0}
				,{1,0}
			}
		}	
	};
	
	private static final int[][][][] expected  = new int[][][][] {
			{// set 0
				{
					{0,0}
					,{0,0}
				}
				,{
					{1,0}
					,{0,0}
				}
			}
			,{// set 1
				{
					{0,0}
					,{0,0}
				}
				,{
					{0,1}
					,{0,0}
				}
			}	
			,{// set 2
				{
					{0,0}
					,{0,0}
				}
				,{
					{0,0}
					,{0,1}
				}
			}	
			,{// set 3
				{
					{0,0}
					,{0,0}
				}
				,{
					{0,0}
					,{1,0}
				}
			}	
			,{// set 4
				{
					{1,0}
					,{0,0}
				}
				,{
					{0,0}
					,{0,0}
				}
			}	
			,{// set 5
				{
					{0,1}
					,{0,0}
				}
				,{
					{0,0}
					,{0,0}
				}
			}	
			,{// set 6
				{
					{0,0}
					,{0,1}
				}
				,{
					{0,0}
					,{0,0}
				}
			}	
			,{// set 7
				{
					{0,0}
					,{1,0}
				}
				,{
					{0,0}
					,{0,0}
				}
			}	
		};
	
	private static final NDFloatArray[] inp;
	private static final NDFloatArray[] exp;	
	
	static {
		// Translate input & expected arrays to NDFloatArrays
		inp = new NDFloatArray[inputs.length];
		exp = new NDFloatArray[expected.length];
		
		for (int s = 0; s < inputs.length; s++) {
			inp[s] = new NDFloatArray(2,2,2);
			for (int z = 0; z < inputs[s].length; z++) {
				for (int y = 0; y < inputs[s][z].length; y++) {
					for (int x = 0; x < inputs[s][z][y].length; x++) {
						inp[s].set(inputs[s][z][y][x], x, y, z);
					}
				}
			}
		}
		
		for (int s = 0; s < expected.length; s++) {
			exp[s] = new NDFloatArray(2,2,2);
			for (int z = 0; z < expected[s].length; z++) {
				for (int y = 0; y < expected[s][z].length; y++) {
					for (int x = 0; x < expected[s][z][y].length; x++) {
						exp[s].set(expected[s][z][y][x], x, y, z);
					}
				}
			}
		}
	}
	
	/**
	 * @param args
	 * @throws Throwable 
	 */
	public static void main(String[] args) throws Throwable {
		/*
		NDFloatArray a = new NDFloatArray(2, 2, 2);
		for (MatrixIterator it = a.iterator(); it.hasNext(); it.next()) {
			if (it.getCurrentCoordinates()[0] == 0
					&& it.getCurrentCoordinates()[1] == 1
					&& it.getCurrentCoordinates()[2] == 0
			) {
				it.set(1.0f);
			} else {
				it.set(0.0f);
			}
		}
		
		int[] coords = getMax(a);
		for (int i = 0; i < coords.length; i++) {
			System.out.print(coords[i] + " ");
		}
		
		System.out.println();
		
		int[] b = {0, 1};
		int[] c = {0, 1};
		int[] d = {1, 0};
		
		System.out.println("intArrayCompare a, b: " + intArrayCompare(b, c));
		System.out.println("intArrayCompare a, c: " + intArrayCompare(c, d));

		System.out.println("getIsCorrect 0, a: " + getIsCorrect(0, a));
		System.out.println("getIsCorrect 7, a: " + getIsCorrect(7, a));
		
		System.out.println("mse 0, a: " + mse(0, a));
		System.out.println("mse , a: " + mse(7, a));
		*/
        try {
            if ( args.length != 1 ) {
//                Evolver.usage();
                System.exit( -1 );
            }

            // Anji Properties class... file must be in classpath
            Properties props = new Properties( args[ 0 ] );
            
            String runID = props.getProperty("run.name") + "-" + System.currentTimeMillis();
            String outputDir = props.getProperty("fitness_function.class") + File.separatorChar + runID + File.separatorChar;
            props.setProperty("run.id", ""+runID);
            props.setProperty("output.dir", outputDir);
            
            Evolver evolver = new Evolver();
            
            evolver.init( props );
            evolver.run();
            System.exit( 0 );
        }
        catch ( Throwable th ) {
//            logger.log(Level.SEVERE, th.getMessage());
            throw th;
        } 
		
	}

	@Override
	public int getMaxFitnessValue() {
		return maxFitnessValue;
	}

	@Override
	protected int evaluate(Chromosome genotype, ActivatorND substrate, int evalThreadIndex) {
		NDFloatArray outs;
        double mse = 0;
        double correctCount = 0;
        
		for (int i = 0; i < inp.length; i++) {
			outs = substrate.next(inp[i]);
            mse += mse(i, outs);   
            correctCount += getIsCorrect(i, outs);
			
		}
	       
        mse /= inputs.length;   
        correctCount /= inputs.length;
       
        genotype.setPerformanceValue((float) correctCount);
       
        double fitness = (mse * 0.5 + correctCount * 0.5 );
       
        return (int) Math.round(fitness * maxFitnessValue);
	}
	

    private static int getIsCorrect(int set, NDFloatArray responses) {
        int[] expPoint = getMax(exp[set]);
        int[] actPoint = getMax(responses);
        return intArrayCompare(actPoint, expPoint) ? 1 : 0;
    }

    private static boolean intArrayCompare(int[] a, int[] b) {
    	if (a.length != b.length) return false;
    	for (int i = 0; i < a.length; i++) {
    		if (a[i] != b[i]) return false;
    	}
    	return true;
    }

    private static double mse(int set, NDFloatArray responses) {
        double err = 0;
        
        for (MatrixIterator it = exp[set].iterator(); it.hasNext(); it.next()) {
        	err += Math.pow(it.get() - responses.get(it.getCurrentCoordinates()), 2);
        }
       
        err = Math.sqrt(err);
       
        return 1 - err / Math.sqrt(responses.size());
    }
   
    private static int[] getMax(NDFloatArray a) {
        int[] coords = null;
        float best = -1;
        
        for (MatrixIterator it = a.iterator(); it.hasNext(); it.next()) {
        	if (null == coords || it.get() > best) {
        		best = it.get();
        		coords = it.getCurrentCoordinates();
        	}
        }
        
        return coords;
    }



	@Override
	protected void scale(int scaleCount, int scaleFactor) {
		
	}

}
